﻿using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.IO;

namespace OpenLS.Spreadsheet.Formats.Biff.Records
{
    internal class Feature11Record : WorksheetLevelRecord
    {
        private readonly List<Ref8U> _refs2 = new List<Ref8U>();
        private FrtRefHeaderU frtRefHeaderU = new FrtRefHeaderU {rt = 0x0872, grbitFrt = FrtFlags.fFrtAlert};
        private SharedFeatureType isf;
        private byte reserved1;
        private uint reserved2;
        private ushort reserved3;
        private TableFeatureType rgbFeat;
        //private byte[] _garbage;

        public Feature11Record(Table table, int id)
        {
            Contract.Requires(table != null);
            frtRefHeaderU.ref8 = (Ref8) table.Range.Address;
            _refs2.Add((Ref8U) table.Range.Address);
            isf = SharedFeatureType.ISFLIST;
            rgbFeat = new TableFeatureType(table, id);
        }

        public Feature11Record()
        {
        }

        internal override BiffRecordType RecordType
        {
            get { return BiffRecordType.Feature11; }
        }

        private byte[] _buffer;
        protected override void ReadContent(BiffReaderContext context, int length)
        {
            this._buffer = context.ReadBytes(length);
        }
        public override void ReadWithContinue()
        {
           //\\ Contract.Assert(!(Next is ContinueFrt11Record));
            List<byte> bytes = new List<byte>(_buffer);
            BiffRecord n = Next;
            while (n is ContinueFrt11Record)
            {
                bytes.AddRange(((ContinueFrt11Record)n).Buffer);
                n = n.Next;
            }
            MemoryStream ms = new MemoryStream(bytes.ToArray());
            BinaryReader reader = new BinaryReader(ms);
            ReadReader(reader, this.Length);
        }
        protected  void ReadReader(BinaryReader context, int length)
        {
#if DEBUG
            long position = context.BaseStream.Position;
#endif
            frtRefHeaderU = new FrtRefHeaderU();
            frtRefHeaderU.Read(context);
            Contract.Assert(frtRefHeaderU.rt == 0x0872);
            isf = (SharedFeatureType) context.ReadUInt16();
            Contract.Assert(isf == SharedFeatureType.ISFLIST);
            reserved1 = context.ReadByte();
            reserved2 = context.ReadUInt32();
            ushort cref2 = context.ReadUInt16();
            uint cbFeatData = context.ReadUInt32();
            if (cbFeatData == 0)
                cbFeatData = (uint) (length - cref2*Ref8U.Size - 27);
            reserved3 = context.ReadUInt16();
            _refs2.Clear();
            for (int i = 0; i < cref2; i++)
            {
                var r = new Ref8U();
                r.Read(context);
                _refs2.Add(r);
            }
            rgbFeat = new TableFeatureType();
            rgbFeat.Read(context);
#if DEBUG
            // Contract.Assert(context.BaseStream.Position == position + length);
            Contract.Assert(Length == length);
#endif
        }

        internal override void Apply(BiffReaderWorksheetContext context)
        {
            if (_refs2.Count != 1)
                return;
            var address = (RangeAddress) _refs2[0];
            var table = new Table(Range.Create(context.Worksheet, address, false));
            table.TotalsRowVisible = rgbFeat.CrwTotals;
            Contract.Assert(table.Columns.Count == rgbFeat.FieldData.Count);
            {
                for (int i = 0; i < table.Columns.Count; i++)
                {
                    table.Columns[i].Name = rgbFeat.FieldData[i].Caption;
                    table.Columns[i].UniqueName = rgbFeat.FieldData[i].StrFieldName;
                }
            }
            foreach (List12RecordItem item in Enumerable.Select(
                GetFollowingRecords<List12Record>(), r => r.rgb))
            {
                if (item is List12BlockLevel)
                {
                    var list12BlockLevel = (List12BlockLevel) item;
                    if (list12BlockLevel.DxfAgg != null)
                        table.TotalsRowDataDifferentialStyleRecord = list12BlockLevel.DxfAgg.ToFormattingRecord(context);
                    if (list12BlockLevel.DxfAggBorder != null)
                        table.TotalsRowBorderDataDifferentialStyleRecord =
                            list12BlockLevel.DxfAggBorder.ToFormattingRecord(context);
                    if (list12BlockLevel.DxfBorder != null)
                        table.TableBorderDataDifferentialStyleRecord = list12BlockLevel.DxfBorder.ToFormattingRecord(context);
                    if (list12BlockLevel.DxfData != null)
                        table.DataDifferentialStyleRecord = list12BlockLevel.DxfData.ToFormattingRecord(context);
                    if (list12BlockLevel.DxfHeader != null)
                        table.HeaderRowDifferentialStyleRecord = list12BlockLevel.DxfHeader.ToFormattingRecord(context);
                    if (list12BlockLevel.DxfHeaderBorder != null)
                        table.HeaderRowBorderDataDifferentialStyleRecord =
                            list12BlockLevel.DxfHeaderBorder.ToFormattingRecord(context);
                }
                else if (item is List12DisplayName)
                {
                    var list12DisplayName = (List12DisplayName) item;
                    table.Name = list12DisplayName.ListName;
                    table.Comment = list12DisplayName.ListComment;
                }
                else if (item is List12TableStyleClientInfo)
                {
                    var c = (List12TableStyleClientInfo) item;
                    string s = c.StListStyleName;
                    if (!string.IsNullOrEmpty(s))
                    {
                        TableStyle style;
                        if (TableStyles.BuiltInStyles.TryGetValue(s, out style))
                        {
                            table.Style = style;
                        }
                        else
                        {
                            Contract.Assert(false, "todo");
                        }
                    }
                    table.ColumnStripesVisible = c.FColumnStripes;
                    table.FirstColumnVisible = c.FFirstColumn;
                    table.LastColumnVisible = c.FLastColumn;
                    table.RowStripesVisible = c.FRowStripes;
                }
            }
            context.Worksheet.Tables.Add(table);
        }

        internal override void WriteContent(BiffWriterWorkbookContext context)
        {
#if DEBUG
            long position = context.Writer.BaseStream.Position;
#endif
            Contract.Assert(frtRefHeaderU.rt == 0x0872);
            frtRefHeaderU.Write(context.Writer);
            context.Write((ushort) isf);
            context.Write(reserved1);
            context.Write(reserved2);
            context.Write((ushort) _refs2.Count);
            uint rgbFeatLength = 0;
            long lengthPosition = context.Position;
            context.Write(rgbFeatLength);
            context.Write(reserved3);
            foreach (Ref8U r in _refs2)
            {
                r.Write(context.Writer);
            }
            long beforeRgb = context.Position;
            rgbFeat.Write(context.Writer);
            rgbFeatLength = (uint) (context.Position - beforeRgb);
            long thisPosition = context.Position;
            context.Position = lengthPosition;
            //\\ context.Write(rgbFeatLength);
            context.Position = thisPosition;
#if DEBUG
            Contract.Assert(context.Position - position < 8000);
#endif
        }
    }
}